#ifndef __ETLBASE_H__
#define __ETLBASE_H__

#ifndef __ETL_H__
	#error etlBase.h requires etl.h to be included first
#endif

#ifndef __ETLSDK_H__
	#error etlBase.h requires etlsdk.h to be included first
#endif

#if LINUX_SYSTEM
#include <xInc/xBaseClass.h>
#else
#include <wInc/wBaseClass.h>
#endif

class LCTimer :
#if LINUX_SYSTEM
public xTimer
#else
public wTimer
#endif
{
};

class LCCritSecLock :
#if LINUX_SYSTEM
public xCritSecLock<true>
#else
public wCritSecLock
#endif
{
};

class LCMapFile :
#if LINUX_SYSTEM
public xMapFile<true>
#else
public wMapFile
#endif
{
};

class LCTmpFile
{
public:
	BOOL CreateFile(LPCTSTR lpszFileName)
	{
		chASSERT(!chIS_EMPTY(lpszFileName) && m_szFileName[0] == '\0');
		_tcscpy(m_szFileName, lpszFileName);
		
		FILE* fp = _tfopen(m_szFileName, _T("a+b"));
		chASSERT(fp != NULL);
		fclose(fp);
		return fp != NULL;
	}
	void DeleteFile()
	{
		chASSERT(m_szFileName[0] != '\0');
		_tunlink(m_szFileName);
		m_szFileName[0] = '\0';
	}
	LPCTSTR GetFileName()
	{
		return m_szFileName;
	}
	LCTmpFile()
	{
		m_szFileName[0] = '\0';
	}
private:
	TCHAR m_szFileName[MAX_PATH];
};

#include "_chAlloc.h"
#include "_chArray.h"
#include "_chString.h"
#include "_chList.h"
#include "_chMap.h"

#pragma warning( push )
#pragma warning(disable : 4700)

template <class TKey, class TVal>
class LCSimpleMap : public CSimpleMap<TKey, TVal>
{
public:
	TVal& operator[] (TKey key)
	{
		int nIndex = FindKey(key);
		if(nIndex == -1)
		{
			TVal tVal(0);
			Add(key, tVal);
			nIndex = m_nSize - 1;
		}
		return GetValueAt(nIndex);
	}
	
};
#pragma warning( pop ) 

template <class TVal>
class LCCritSecList : public chObjList<TVal>
		, public LCCritSecLock
{
};

template <class TKey, class TVal>
class LCCritSecMap : public chObjMap<TKey, TVal>
		, public LCCritSecLock
{
};

inline chTempCHARArray chW2A(LPCWSTR lpszText, UINT pageCode = CP_ACP)
{
	chASSERT(lpszText != NULL);
	int nCharSize = etlWideCharToMultiByte(pageCode, 0, lpszText, -1, NULL, 0, 0, 0);
	chTempCHARArray tCHARArray('\0', nCharSize);
	etlWideCharToMultiByte(pageCode, 0, lpszText, -1, (CHAR*)tCHARArray, nCharSize, NULL, NULL);
	return tCHARArray;
}

inline chTempWCHARArray chA2W(LPCSTR lpszText, UINT pageCode = CP_ACP)
{
	chASSERT(lpszText != NULL);
	int nWCharSize = etlMultiByteToWideChar(pageCode, 0, lpszText, -1, NULL, 0);
	chTempWCHARArray tWCHARArray(L'\0', nWCharSize);
	etlMultiByteToWideChar(pageCode, 0, lpszText, -1, (WCHAR*)tWCHARArray, nWCharSize);
	return tWCHARArray;
}

inline chTempString chA2T(LPCSTR lpszText)
{
	chASSERT(lpszText != NULL);
#ifdef _UNICODE
	int nWCharSize = etlMultiByteToWideChar(CP_ACP, 0, lpszText, -1, NULL, 0);
	chTempString tBufString;
	etlMultiByteToWideChar(CP_ACP, 0, lpszText, -1, tBufString.GetBufferSetLength(nWCharSize), nWCharSize);
	return tBufString;
#else
	return chTempString(lpszText);
#endif
}

inline chTempString chW2T(LPCWSTR lpszText)
{
	chASSERT(lpszText != NULL);
#ifdef _UNICODE
	return chTempString(lpszText);
#else
	int nCharSize = etlWideCharToMultiByte(CP_ACP, 0, lpszText, -1, NULL, 0, 0, 0);
	chTempString tBufString;
	etlWideCharToMultiByte(CP_ACP, 0, lpszText, -1, tBufString.GetBufferSetLength(nCharSize), nCharSize, NULL, NULL);
	return tBufString;
#endif
}

inline chTempWCHARArray chT2W(LPCTSTR lpszText)
{
	chASSERT(lpszText != NULL);
#ifdef _UNICODE
	return chTempWCHARArray(lpszText, _tcslen(lpszText) + 1);
#else
	return chA2W(lpszText);
#endif
}

inline chTempCHARArray chT2A(LPCTSTR lpszText)
{
	chASSERT(lpszText != NULL);
#ifdef _UNICODE
	return chW2A(lpszText);
#else
	return chTempCHARArray(lpszText, _tcslen(lpszText) + 1);
#endif
}

/////////////////////////////////////////////////////////////////////////////
// LCTimeSpan and LCTime
#define maxTimeBufferSize       128

class LCTimeSpan
{
	time_t m_timeSpan;
	friend class LCTime;
public:
	LCTimeSpan::LCTimeSpan()
	{ }
	LCTimeSpan::LCTimeSpan(time_t time)
	{ m_timeSpan = time; }
	LCTimeSpan::LCTimeSpan(LONG lDays, int nHours, int nMins, int nSecs)
	{ m_timeSpan = nSecs + 60* (nMins + 60* (nHours + 24* lDays)); }
	LCTimeSpan::LCTimeSpan(const LCTimeSpan& timeSpanSrc)
	{ m_timeSpan = timeSpanSrc.m_timeSpan; }
	const LCTimeSpan& LCTimeSpan::operator=(const LCTimeSpan& timeSpanSrc)
	{ m_timeSpan = timeSpanSrc.m_timeSpan; return *this; }
	LONG LCTimeSpan::GetDays() const
	{ return m_timeSpan / (24*3600L); }
	LONG LCTimeSpan::GetTotalHours() const
	{ return m_timeSpan/3600; }
	int LCTimeSpan::GetHours() const
	{ return (int)(GetTotalHours() - GetDays()*24); }
	LONG LCTimeSpan::GetTotalMinutes() const
	{ return m_timeSpan/60; }
	int LCTimeSpan::GetMinutes() const
	{ return (int)(GetTotalMinutes() - GetTotalHours()*60); }
	LONG LCTimeSpan::GetTotalSeconds() const
	{ return m_timeSpan; }
	int LCTimeSpan::GetSeconds() const
	{ return (int)(GetTotalSeconds() - GetTotalMinutes()*60); }
	LCTimeSpan LCTimeSpan::operator-(LCTimeSpan timeSpan) const
	{ return LCTimeSpan(m_timeSpan - timeSpan.m_timeSpan); }
	LCTimeSpan LCTimeSpan::operator+(LCTimeSpan timeSpan) const
	{ return LCTimeSpan(m_timeSpan + timeSpan.m_timeSpan); }
	const LCTimeSpan& LCTimeSpan::operator+=(LCTimeSpan timeSpan)
	{ m_timeSpan += timeSpan.m_timeSpan; return *this; }
	const LCTimeSpan& LCTimeSpan::operator-=(LCTimeSpan timeSpan)
	{ m_timeSpan -= timeSpan.m_timeSpan; return *this; }
	BOOL LCTimeSpan::operator==(LCTimeSpan timeSpan) const
	{ return m_timeSpan == timeSpan.m_timeSpan; }
	BOOL LCTimeSpan::operator!=(LCTimeSpan timeSpan) const
	{ return m_timeSpan != timeSpan.m_timeSpan; }
	BOOL LCTimeSpan::operator<(LCTimeSpan timeSpan) const
	{ return m_timeSpan < timeSpan.m_timeSpan; }
	BOOL LCTimeSpan::operator>(LCTimeSpan timeSpan) const
	{ return m_timeSpan > timeSpan.m_timeSpan; }
	BOOL LCTimeSpan::operator<=(LCTimeSpan timeSpan) const
	{ return m_timeSpan <= timeSpan.m_timeSpan; }
	BOOL LCTimeSpan::operator>=(LCTimeSpan timeSpan) const
	{ return m_timeSpan >= timeSpan.m_timeSpan; }
	
	chTempString Format(LPCTSTR pFormat) const
		// formatting timespans is a little trickier than formatting LCTimes
		//  * we are only interested in relative time formats, ie. it is illegal
		//      to format anything dealing with absolute time (i.e. years, months,
		//         day of week, day of year, timezones, ...)
		//  * the only valid formats:
		//      %D - # of days -- NEW !!!
		//      %H - hour in 24 hour format
		//      %M - minute (0-59)
		//      %S - seconds (0-59)
		//      %% - percent sign
	{
		TCHAR szBuffer[maxTimeBufferSize];
		TCHAR ch;
		LPTSTR pch = szBuffer;
		
		while ((ch = *pFormat++) != '\0')
		{
			chASSERT(pch < &szBuffer[maxTimeBufferSize]);
			if (ch == '%')
			{
				switch (ch = *pFormat++)
				{
				default:
					chASSERT(FALSE);      // probably a bad format character
				case '%':
					*pch++ = ch;
					break;
				case 'D':
					pch += wsprintf(pch, _T("%ld"), GetDays());
					break;
				case 'H':
					pch += wsprintf(pch, _T("%02d"), GetHours());
					break;
				case 'M':
					pch += wsprintf(pch, _T("%02d"), GetMinutes());
					break;
				case 'S':
					pch += wsprintf(pch, _T("%02d"), GetSeconds());
					break;
				}
			}
			else
			{
				*pch++ = ch;
				if (_istlead(ch))
				{
					chASSERT(pch < &szBuffer[maxTimeBufferSize]);
					*pch++ = *pFormat++;
				}
			}
		}
		
		*pch = '\0';
		return szBuffer;
	}
};

class LCTime
{
	time_t m_time;
public:
	// Constructors
	LCTime::LCTime()
	{ }
	LCTime::LCTime(const LCTime& timeSrc)
	{ m_time = timeSrc.m_time; }
	LCTime(time_t time)
	{ m_time = time; }
	LCTime(int nYear, int nMonth, int nDay, int nHour, int nMin, int nSec, int nDST = -1)
	{
		struct tm atm;
		atm.tm_sec = nSec;
		atm.tm_min = nMin;
		atm.tm_hour = nHour;
		chASSERT(nDay >= 1 && nDay <= 31);
		atm.tm_mday = nDay;
		chASSERT(nMonth >= 1 && nMonth <= 12);
		atm.tm_mon = nMonth - 1;        // tm_mon is 0 based
		chASSERT(nYear >= 1900);
		atm.tm_year = nYear - 1900;     // tm_year is 1900 based
		atm.tm_isdst = nDST;
		m_time = mktime(&atm);
		chASSERT(m_time != -1);       // indicates an illegal input time
	}
	
	LCTime(const SYSTEMTIME& sysTime, int nDST = -1)
	{
		if (sysTime.wYear < 1900)
		{
			time_t time0 = 0L;
			LCTime timeT(time0);
			*this = timeT;
		}
		else
		{
			LCTime timeT(
				(int)sysTime.wYear, (int)sysTime.wMonth, (int)sysTime.wDay,
				(int)sysTime.wHour, (int)sysTime.wMinute, (int)sysTime.wSecond,
				nDST);
			*this = timeT;
		}
	}
	
	static LCTime PASCAL GetCurrentTime()
	{
		return LCTime(::time(NULL));
	}
	
	struct tm* GetLocalTm(struct tm* ptm) const
	{
		if (ptm != NULL)
		{
			struct tm* ptmTemp = localtime(&m_time);
			if (ptmTemp == NULL)
				return NULL;    // indicates the m_time was not initialized!
			
			*ptm = *ptmTemp;
			return ptm;
		}
		else
			return localtime(&m_time);
	}
	const LCTime& LCTime::operator=(const LCTime& timeSrc)
	{ m_time = timeSrc.m_time; return *this; }
	int LCTime::GetYear() const
	{ return (GetLocalTm(NULL)->tm_year) + 1900; }
	int LCTime::GetMonth() const
	{ return GetLocalTm(NULL)->tm_mon + 1; }
	int LCTime::GetDay() const
	{ return GetLocalTm(NULL)->tm_mday; }
	int LCTime::GetHour() const
	{ return GetLocalTm(NULL)->tm_hour; }
	int LCTime::GetMinute() const
	{ return GetLocalTm(NULL)->tm_min; }
	int LCTime::GetSecond() const
	{ return GetLocalTm(NULL)->tm_sec; }
	int LCTime::GetDayOfWeek() const
	{ return GetLocalTm(NULL)->tm_wday + 1; }
	LCTimeSpan LCTime::operator-(LCTime time) const
	{ return LCTimeSpan(m_time - time.m_time); }
	LCTime LCTime::operator-(LCTimeSpan timeSpan) const
	{ return LCTime(m_time - timeSpan.m_timeSpan); }
	LCTime LCTime::operator+(LCTimeSpan timeSpan) const
	{ return LCTime(m_time + timeSpan.m_timeSpan); }
	const LCTime& LCTime::operator+=(LCTimeSpan timeSpan)
	{ m_time += timeSpan.m_timeSpan; return *this; }
	const LCTime& LCTime::operator-=(LCTimeSpan timeSpan)
	{ m_time -= timeSpan.m_timeSpan; return *this; }
	BOOL LCTime::operator==(LCTime time) const
	{ return m_time == time.m_time; }
	BOOL LCTime::operator!=(LCTime time) const
	{ return m_time != time.m_time; }
	BOOL LCTime::operator<(LCTime time) const
	{ return m_time < time.m_time; }
	BOOL LCTime::operator>(LCTime time) const
	{ return m_time > time.m_time; }
	BOOL LCTime::operator<=(LCTime time) const
	{ return m_time <= time.m_time; }
	BOOL LCTime::operator>=(LCTime time) const
	{ return m_time >= time.m_time; }
	
	chTempString Format(LPCTSTR pFormat) const
	{
		TCHAR szBuffer[maxTimeBufferSize];
		
		struct tm* ptmTemp = localtime(&m_time);
		if (ptmTemp == NULL ||
			!_tcsftime(szBuffer, maxTimeBufferSize, pFormat, ptmTemp))
			szBuffer[0] = '\0';
		return szBuffer;
	}
};

class LCLocalTime : public SYSTEMTIME
{
public:
	LCLocalTime()
	{
		etlGetLocalTime(this);
	}
};

typedef TCHAR				ATOMName[MAX_PATH];
class uCAtomHelper : public chSimpleMap<ATOM, ATOMName, 128>
{
	typedef chSimpleMap<ATOM, ATOMName, 128>	mapATOMGroup;
public:
	ATOM AddAnonyAtom()
	{
		return PrivateAddAtom(NULL);
	}
	ATOM AddAtom(LPCTSTR lpszAtomName)
	{
		chASSERT(!chIS_EMPTY(lpszAtomName));
		ATOM atom = FindAtom(lpszAtomName);
		if(atom == 0)
			atom = PrivateAddAtom(lpszAtomName);
		return atom;
	}
	ATOM FindAtom(LPCTSTR lpszAtomName)
	{
		chASSERT(!chIS_EMPTY(lpszAtomName));
		int i = 0;
		for(i = 0; i < m_nElementCount; i++)
		{
			if(m_aT[i].pNextFreeItem == NULL && _tcsncmp(m_aT[i].value, lpszAtomName, chLENOF(m_aT[i].value)) == 0)
				return m_aT[i].key;
		}
		return 0;
	}
	void DeleteAtom(ATOM nAtom)
	{
		mapATOMGroup::erase(nAtom);
	}
	LPCTSTR GetAtomName(ATOM nAtom)
	{
		int iIndex = mapATOMGroup::find(nAtom);
		if(iIndex == -1)
			return NULL;
		return mapATOMGroup::m_aT[iIndex].value;
	}
private:
	ATOM PrivateAddAtom(LPCTSTR lpszAtomName)
	{
		int iIndex = mapATOMGroup::AllocOneNode();
		if(lpszAtomName != NULL)
			_tcsncpy(m_aT[iIndex].value, lpszAtomName, chLENOF(m_aT[iIndex].value));
		mapATOMGroup::m_aT[iIndex].key = iIndex + 0xC000;
		return iIndex + 0xC000;
	}
};

#endif //__ETLBASE_H__
